package com.kexio.auth.tenant;

import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.kexio.auth.config.SecurityProperties;
import com.kexio.auth.context.TenantContextHolder;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletRequest;

/**
 * 默认租户解析器实现
 * 
 * 按以下优先级顺序解析租户ID：
 * 1. JWT Token中的 "tenant_id" 或 "tenantId" 字段（优先级：100）
 * 2. HTTP请求头中的 "X-Tenant-Id" 或 "Tenant-Id"（优先级：80）
 * 3. URL参数中的 "tenantId" 或 "tenant"（优先级：60）
 * 4. 子域名解析（如：tenant1.example.com → tenant1）（优先级：40）
 * 5. 配置的默认租户ID（优先级：0）
 * 
 * 安全特性：
 * - 租户ID格式验证：只允许字母、数字、下划线、横线
 * - 长度限制：最大64个字符
 * - 防止恶意租户ID：过滤保留字和特殊字符
 * - 规范化处理：自动转换为小写并去除空格
 * 
 * 性能优化：
 * - 请求级缓存：避免同一请求重复解析
 * - 快速失败：无效输入立即返回默认值
 * - 延迟解析：只在需要时才进行复杂的解析操作
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Component
public class DefaultTenantResolver implements TenantResolver {
    
    private static final Logger logger = LoggerFactory.getLogger(DefaultTenantResolver.class);
    
    // ==================== 配置常量 ====================
    
    /**
     * JWT中租户ID的字段名（按优先级排序）
     */
    private static final String[] JWT_TENANT_CLAIMS = {"tenant_id", "tenantId", "tid"};
    
    /**
     * HTTP请求头中租户ID的名称（按优先级排序）
     */
    private static final String[] HEADER_TENANT_NAMES = {
        "X-Tenant-Id", "X-Tenant", "Tenant-Id", "Tenant"
    };
    
    /**
     * URL参数中租户ID的名称（按优先级排序）
     */
    private static final String[] PARAMETER_TENANT_NAMES = {"tenantId", "tenant", "tid"};
    
    /**
     * 子域名提取的正则表达式
     * 匹配格式：tenant.example.com、tenant-env.example.com
     */
    private static final Pattern SUBDOMAIN_PATTERN = Pattern.compile("^([a-zA-Z0-9-]+)\\..*");
    
    /**
     * 请求属性中缓存解析结果的键名
     */
    private static final String CACHE_ATTRIBUTE_KEY = "_tenant_resolver_cache";
    
    // ==================== 依赖注入 ====================
    
    // ❌ 已删除：JwtProvider - Sa-Token内置Token处理

    // ❌ 已删除：JWT架构下租户验证逻辑简化
    // private com.kexio.auth.spi.TenantValidator tenantValidator;

    @Autowired(required = false)
    private SecurityProperties securityProperties;

    @Value("${kexio.auth.tenant.jwt-claims:tenant_id,tenantId,tid}")
    private String jwtClaimsConfig;
    
    @Value("${kexio.auth.tenant.header-names:X-Tenant-Id,X-Tenant,Tenant-Id,Tenant}")
    private String headerNamesConfig;
    
    @Value("${kexio.auth.tenant.param-names:tenantId,tenant,tid}")
    private String paramNamesConfig;
    
    @Value("${kexio.auth.tenant.subdomain-ignore:www,api,admin,static,cdn,assets,mail,ftp,blog,docs}")
    private String subdomainIgnoreConfig;
    
    // ==================== 核心接口实现 ====================
    
    @Override
    public String resolve(HttpServletRequest request) {
        // 1. 检查请求级缓存
        String cached = getCachedResult(request);
        if (cached != null) {
            logger.trace("使用缓存的租户解析结果: {}", cached);
            return cached;
        }
        
        // 2. 执行解析
        String tenantId = performResolve(request);
        
        // 2.5 若关闭多租户，直接回退默认租户并跳过后续存在性校验
        boolean tenantEnabled = false;
        boolean allowFallback = true;
        try {
            tenantEnabled = securityProperties != null && securityProperties.getTenant() != null && securityProperties.getTenant().isEnabled();
            allowFallback = securityProperties == null || securityProperties.getTenant() == null || securityProperties.getTenant().isAllowDefaultFallback();
        } catch (Exception ignore) {}
        if (!tenantEnabled) {
            tenantId = getDefaultTenantId();
            setCachedResult(request, tenantId);
            logger.debug("多租户关闭，直接使用默认租户: {}", tenantId);
            return tenantId;
        }

        // 3. 确保结果不为null（遵循配置：启用多租户且不允许默认回退时，不回退default）
        if (tenantId == null) {
            if (allowFallback) {
                tenantId = getDefaultTenantId();
            }
        }

        // ❌ 已简化：JWT架构下租户验证逻辑简化，直接使用解析出的tenantId
        /*
        // 校验存在性与状态（若提供了 TenantValidator）
        if (tenantEnabled && tenantValidator != null && tenantId != null) {
            try {
                if (!tenantValidator.exists(tenantId)) {
                    logger.warn("解析出的租户不存在或已删除: {}", tenantId);
                    tenantId = allowFallback ? getDefaultTenantId() : null;
                } else {
                    Integer status = tenantValidator.getStatus(tenantId);
                    if (status == null || status == 0 || status == 3) { // 0-禁用,3-过期
                        logger.warn("租户状态不可用: {} (status={})", tenantId, status);
                        tenantId = allowFallback ? getDefaultTenantId() : null;
                    }
                }
            } catch (Exception e) {
                logger.warn("租户状态校验失败: {}", e.getMessage());
                tenantId = allowFallback ? getDefaultTenantId() : null;
            }
        }
        */
        
        // 4. 缓存结果
        setCachedResult(request, tenantId);
        
        logger.debug("租户解析完成: {} (请求: {} {})", 
            tenantId, request.getMethod(), request.getRequestURI());
        
        return tenantId;
    }
    
    @Override
    public String tryResolve(HttpServletRequest request) {
        // 检查缓存
        String cached = getCachedResult(request);
        if (cached != null && !cached.equals(getDefaultTenantId())) {
            return cached;
        }
        
        // 执行解析（不使用默认值）
        return performResolve(request);
    }
    
    @Override
    public String getDefaultTenantId() {
        return TenantContextHolder.DEFAULT_TENANT_ID;
    }
    
    @Override
    public boolean isValidTenantId(String tenantId) {
        try {
            TenantContextHolder.validateTenantId(tenantId);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
    
    @Override
    public String normalizeTenantId(String tenantId) {
        return TenantContextHolder.normalizeTenantId(tenantId);
    }
    
    @Override
    public List<TenantSource> getSupportedSources() {
        return Arrays.asList(TenantSource.values());
    }
    
    // ==================== 内部解析逻辑 ====================
    
    /**
     * 执行租户解析的核心逻辑
     */
    private String performResolve(HttpServletRequest request) {
        // 并行采集各来源候选值
        String jwtTenant = resolveFromJwt(request);
        String headerTenant = resolveFromHeaders(request);
        String paramTenant = resolveFromParameters(request);
        String subdomainTenant = resolveFromSubdomain(request);

        // 统计不同非空候选，记录冲突告警
        java.util.LinkedHashMap<String, String> candidates = new java.util.LinkedHashMap<>();
        if (StringUtils.hasText(jwtTenant)) candidates.put("JWT", jwtTenant);
        if (StringUtils.hasText(headerTenant)) candidates.put("HEADER", headerTenant);
        if (StringUtils.hasText(paramTenant)) candidates.put("PARAM", paramTenant);
        if (StringUtils.hasText(subdomainTenant)) candidates.put("SUBDOMAIN", subdomainTenant);

        if (candidates.size() > 1) {
            java.util.Set<String> distinct = new java.util.HashSet<>(candidates.values());
            if (distinct.size() > 1) {
                logger.warn("租户来源冲突: {} (请求: {} {})", candidates, request.getMethod(), request.getRequestURI());
            }
        }

        // 按优先级选择最终租户
        String tenantId = null;
        if (isValidTenantId(jwtTenant)) tenantId = jwtTenant;
        else if (isValidTenantId(headerTenant)) tenantId = headerTenant;
        else if (isValidTenantId(paramTenant)) tenantId = paramTenant;
        else if (isValidTenantId(subdomainTenant)) tenantId = subdomainTenant;

        if (StringUtils.hasText(tenantId)) {
            logger.debug("租户解析完成(优先级选择): {}", tenantId);
            return normalizeTenantId(tenantId);
        }

        logger.trace("未能从请求中解析出有效的租户ID");
        return null;
    }
    
    /**
     * 从Sa-Token Session中解析租户ID (优化版本)
     */
    private String resolveFromJwt(HttpServletRequest request) {
        try {
            // Sa-Token: 直接从Session获取租户ID
            if (StpUtil.isLogin()) {
                String tenantId = (String) StpUtil.getSession().get("tenantId");
                if (StringUtils.hasText(tenantId)) {
                    logger.trace("从Sa-Token Session解析到租户ID: {}", tenantId);
                    return tenantId;
                }
            }
            
            // 备用：从Authorization头解析（保持兼容性）
            String authHeader = request.getHeader("Authorization");
            if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
                // Sa-Token会自动处理Token，这里只是为了日志记录
                logger.trace("检测到Authorization头，但Sa-Token未找到已登录会话");
            }
            
        } catch (Exception e) {
            logger.debug("Sa-Token租户解析失败: {}", e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 从HTTP请求头中解析租户ID
     */
    private String resolveFromHeaders(HttpServletRequest request) {
        String[] headerNames = getConfiguredHeaderNames();
        for (String headerName : headerNames) {
            String tenantId = request.getHeader(headerName);
            if (StringUtils.hasText(tenantId)) {
                logger.trace("从请求头 '{}' 解析到租户ID: {}", headerName, tenantId);
                return tenantId;
            }
        }
        return null;
    }
    
    /**
     * 从URL参数中解析租户ID
     */
    private String resolveFromParameters(HttpServletRequest request) {
        String[] paramNames = getConfiguredParamNames();
        for (String paramName : paramNames) {
            String tenantId = request.getParameter(paramName);
            if (StringUtils.hasText(tenantId)) {
                logger.trace("从URL参数 '{}' 解析到租户ID: {}", paramName, tenantId);
                return tenantId;
            }
        }
        return null;
    }
    
    /**
     * 从子域名中解析租户ID
     */
    private String resolveFromSubdomain(HttpServletRequest request) {
        try {
            // 获取Host头
            String host = request.getHeader("Host");
            if (!StringUtils.hasText(host)) {
                return null;
            }
            
            // 移除端口号
            int colonIndex = host.indexOf(':');
            if (colonIndex > 0) {
                host = host.substring(0, colonIndex);
            }
            
            // 使用正则表达式提取子域名
            java.util.regex.Matcher matcher = SUBDOMAIN_PATTERN.matcher(host);
            if (matcher.matches()) {
                String subdomain = matcher.group(1);
                
                // 过滤掉常见的非租户子域名
                if (!isCommonSubdomain(subdomain)) {
                    logger.trace("从子域名解析到租户ID: {} (Host: {})", subdomain, host);
                    return subdomain;
                }
            }
            
        } catch (Exception e) {
            logger.debug("子域名租户解析失败: {}", e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 检查是否为常见的非租户子域名
     */
    private boolean isCommonSubdomain(String subdomain) {
        String lowerSubdomain = subdomain.toLowerCase();
        String[] ignores = getConfiguredSubdomainIgnores();
        for (String ig : ignores) {
            if (lowerSubdomain.equals(ig.trim().toLowerCase())) {
                return true;
            }
        }
        return false;
    }

    private String[] getConfiguredJwtClaims() {
        if (!StringUtils.hasText(jwtClaimsConfig)) return JWT_TENANT_CLAIMS;
        return jwtClaimsConfig.split(",");
    }
    private String[] getConfiguredHeaderNames() {
        if (!StringUtils.hasText(headerNamesConfig)) return HEADER_TENANT_NAMES;
        return headerNamesConfig.split(",");
    }
    private String[] getConfiguredParamNames() {
        if (!StringUtils.hasText(paramNamesConfig)) return PARAMETER_TENANT_NAMES;
        return paramNamesConfig.split(",");
    }
    private String[] getConfiguredSubdomainIgnores() {
        if (!StringUtils.hasText(subdomainIgnoreConfig)) return new String[]{"www","api","admin","static","cdn","assets","mail","ftp","blog","docs"};
        return subdomainIgnoreConfig.split(",");
    }
    
    // ==================== 缓存管理 ====================
    
    /**
     * 获取请求级缓存的解析结果
     */
    private String getCachedResult(HttpServletRequest request) {
        Object cached = request.getAttribute(CACHE_ATTRIBUTE_KEY);
        return cached instanceof String ? (String) cached : null;
    }
    
    /**
     * 设置请求级缓存的解析结果
     */
    private void setCachedResult(HttpServletRequest request, String tenantId) {
        request.setAttribute(CACHE_ATTRIBUTE_KEY, tenantId);
    }
    
    // ==================== 配置和管理方法 ====================
    
    /**
     * 获取当前配置的JWT租户字段名
     */
    public String[] getJwtTenantClaims() {
        return JWT_TENANT_CLAIMS.clone();
    }
    
    /**
     * 获取当前配置的请求头租户名称
     */
    public String[] getHeaderTenantNames() {
        return HEADER_TENANT_NAMES.clone();
    }
    
    /**
     * 获取当前配置的URL参数租户名称
     */
    public String[] getParameterTenantNames() {
        return PARAMETER_TENANT_NAMES.clone();
    }
    
    /**
     * 检查请求是否包含租户信息
     * 
     * @param request HTTP请求
     * @return true如果包含租户信息，false否则
     */
    public boolean hasTenantInfo(HttpServletRequest request) {
        return tryResolve(request) != null;
    }
    
    /**
     * 获取租户解析的诊断信息
     * 
     * @param request HTTP请求
     * @return 诊断信息字符串
     */
    public String getDiagnosticInfo(HttpServletRequest request) {
        StringBuilder info = new StringBuilder("租户解析诊断信息:\n");
        
        // JWT信息
        String jwtTenant = resolveFromJwt(request);
        info.append("  JWT: ").append(jwtTenant != null ? jwtTenant : "未找到").append("\n");
        
        // 请求头信息
        String headerTenant = resolveFromHeaders(request);
        info.append("  Header: ").append(headerTenant != null ? headerTenant : "未找到").append("\n");
        
        // URL参数信息
        String paramTenant = resolveFromParameters(request);
        info.append("  Parameter: ").append(paramTenant != null ? paramTenant : "未找到").append("\n");
        
        // 子域名信息
        String subdomainTenant = resolveFromSubdomain(request);
        info.append("  Subdomain: ").append(subdomainTenant != null ? subdomainTenant : "未找到").append("\n");
        
        // 最终结果
        String finalTenant = resolve(request);
        info.append("  最终结果: ").append(finalTenant);
        
        return info.toString();
    }
}
